#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>

// Todos usan 64 bits para DES y 128 para AES

//#define cipherFun EVP_aes_128_cbc()
//#define cipherFun EVP_aes_128_ecb()
//#define cipherFun EVP_aes_128_cfb128()
//#define cipherFun EVP_aes_128_ofb()
//#define cipherFun EVP_des_cbc()
//#define cipherFun EVP_des_cbc()
//#define cipherFun EVP_des_cfb()
//#define cipherFun EVP_des_ofb()

enum{ AES, DES };
enum{ ECB, CFB, OFB, CBC};
typedef const EVP_CIPHER *(*cipherT)(void);
cipherT ciphers[8] = {
	EVP_aes_128_ecb, EVP_aes_128_cfb128, EVP_aes_128_ofb, EVP_aes_128_cbc,
	EVP_des_ecb, EVP_des_cfb, EVP_des_ofb, EVP_des_cbc
};

int
encrypt( unsigned char **encryptedText, unsigned char *plainText, int plainTextSize, cipherT cipher, unsigned char *mykey, unsigned char *iv )
{
	int encryptedTextSize, paddingResult;
	int termTextSize = 0;
	EVP_CIPHER_CTX ctx;
	
	if( ( *encryptedText = malloc( plainTextSize) ) == NULL )
		return 1;

    EVP_EncryptInit(&ctx, cipher(), mykey, iv);
    EVP_CIPHER_CTX_set_padding(&ctx, 0);

    EVP_EncryptUpdate(&ctx, *encryptedText, &encryptedTextSize, plainText, plainTextSize);
    //printf("\tencrypt(): in_len = %d \t bits = %d\n", plainTextSize, plainTextSize*8);//DEBUG
    //printf("\tencrypt(): out_len = %d \t bits = %d\n", encryptedTextSize, encryptedTextSize*8);//DEBUG
    paddingResult = EVP_EncryptFinal(&ctx, &(*encryptedText)[encryptedTextSize], &termTextSize);
    //printf("\tencrypt(): term_len = %d \t bits = %d\n", termTextSize, termTextSize*8);
    EVP_cleanup();
    if( paddingResult == 0 )
    	return 2;

    return 0;
}

int
decrypt( unsigned char **decryptedText, unsigned char *encryptedText, int encryptedTextSize, cipherT cipher, unsigned char *mykey, unsigned char*iv )
{
	int decryptedTextSize, paddingResult ;
	EVP_CIPHER_CTX ctx;
	int termTextSize = 0;
		

	if( ( *decryptedText = malloc( encryptedTextSize) ) == NULL )
		return 1;
	EVP_DecryptInit(&ctx, cipher(), mykey, iv);
	EVP_DecryptUpdate(&ctx, *decryptedText, &decryptedTextSize, encryptedText, encryptedTextSize);
	//printf("\tdecrypt(): in_len = %d \t bits = %d\n", decryptedTextSize, decryptedTextSize*8);
	//printf("decrypt(): decrypted = [%s]\n", *decryptedText);
	paddingResult = EVP_DecryptFinal(&ctx, &(*decryptedText)[decryptedTextSize], &termTextSize);
	EVP_cleanup();
	//printf("\tdecrypt(): LAST in_len = %d \t bits = %d\n", termTextSize, termTextSize*8);
	if( paddingResult == 0 )
		return 2;
	return 0;
}

int
main(void)
{
	unsigned char plaintext[1024] = "Cataratas del Iguazu Misiones Argentinas,(20,2,200). Glaciar Perito Moreno Provincia de Santa Cruz Argentine.123";
	unsigned char *unciphertext;
    unsigned char *ciphertext;
    int errorCode;
    cipherT cipher = ciphers[AES*4 + CBC];

    unsigned char iv[EVP_MAX_IV_LENGTH];
    unsigned char key[16] = {0x12, 0x34, 0x56, 0x78, 0x91, 0x12, 0x34, 0x56,0x78, 0x92, 0x12, 0x34, 0x56, 0x78, 0x93, 0x12};
    memset(iv, 0x00, EVP_MAX_IV_LENGTH);

    printf("Plain text: [%s]\n", plaintext);
    int plain_len = strlen((char*)plaintext);
    printf("Plain text length = %d \tbits = %d\n", plain_len, plain_len*8);

    if( (errorCode=encrypt( &ciphertext, plaintext, plain_len, cipher, key, iv )) != 0 )
    	printf("Encrypt error. Code %d", errorCode);


    printf("\nDECRYPT");
    printf("\n=======\n");
    decrypt( &unciphertext, ciphertext, plain_len, cipher, key, iv );
    printf("Decrypted fn1: [%s]\n", unciphertext);
    free( unciphertext);
    free( ciphertext );
    return 0;
}

